/*
 * =====================================================================================
 *
 *       Filename:  geneticAlgorithm.cpp
 *
 *    Description:  Class for genetic algorithm, used for character generation
 *
 *        Version:  1.0
 *        Created:  29/06/12 18:25:07
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Moos Hueting (mh), mooshueting@gmail.com
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include "geneticAlgorithm.h"

GA::GA(int popSize, int _charSize) {
    // set character size
    charSize = _charSize;
    // seed random generator
    srand(time(NULL));
    gen = boost::random::mt19937(time(NULL));

    // reserve space
    comp = vector<double>(popSize, 0.0);
    population = vector<Mat>(popSize);
    prevPopulation = vector<Mat>(popSize);

    // initialize matrices
    int i;
    for (i=0; i < popSize; i++) {
        population.at(i) = Mat(charSize, charSize, CV_32FC1);
        prevPopulation.at(i) = Mat(charSize, charSize, CV_32FC1);
    }

    // randomly populate current population
    RNG rng(rand());

    for (i=0; i < popSize; i++) {
        rng.fill(population.at(i), RNG::UNIFORM, 0, 1);
        threshold(population.at(i), population.at(i), 0.5, 1, THRESH_BINARY);
    }

    sortComplexities(); // sort population by complexity
    updateComplexities(); // update complexities of current population
}

void GA::updateComplexities() {
    unsigned int i;
    for (i=0; i < population.size(); i++)
        comp.at(i) = complexity(population.at(i));
}

void GA::sortComplexities() {
    sort(population.begin(), population.end(), complexCompare);
}

void GA::advance() {
    prevPopulation.erase(prevPopulation.begin(), prevPopulation.end()); // destroy old generation
    unsigned int i;
    for (i=0; i < population.size(); i++)
        prevPopulation.push_back(population.at(i).clone()); // make new generation old generation
    population.erase(population.begin(), population.end()); // blank slate
}

void GA::printComplexities() {
    unsigned int i;
    for (i=0; i < population.size(); i++)
        cout << comp.at(i) << endl;
}

void GA::crossOver(int a, int b) {
    float temp;

    boost::random::uniform_int_distribution<> dist(0, charSize*charSize);

    int c = dist(gen);
    int i;

    // do crossover
    for (i=0; i<c; i++) {
        temp = population.at(a).at<float>(i);
        population.at(a).at<float>(i) = population.at(b).at<float>(i);
        population.at(b).at<float>(i) = temp;
    }

}

void GA::mutate(int a) {
    int i;
    boost::random::uniform_int_distribution<> dist(0, charSize*charSize);

    for (i=0; i < charSize*charSize; i++) {
        if (dist(gen) > 97) {
            if (population.at(a).at<float>(i) > 0.9) {
                population.at(a).at<float>(i) = 0.0;
            } else {
                population.at(a).at<float>(i) = 1.0;
            }
        }
    }
}

void GA::display(int a) {
    namedWindow("Display Image", CV_WINDOW_AUTOSIZE);
    Mat disp;
    resize(population.at(a), disp, Size(0,0), 10, 10, INTER_NEAREST);
    imshow("Display Image", disp);
    waitKey(0);
    destroyWindow("Display Image");
}

void GA::evolve() {
    advance(); // copy current population to old population

    // elitism (copy best two individuals directly)
    population.push_back(prevPopulation.at(0).clone());
    population.push_back(prevPopulation.at(1).clone());

    boost::random::discrete_distribution<> dist(comp);

    unsigned int i;
    for (i=0; i < (prevPopulation.size() - 2) / 2; i++) {
        population.push_back(prevPopulation.at(dist(gen)).clone()); 
        population.push_back(prevPopulation.at(dist(gen)).clone()); 
        crossOver(i * 2 + 2, i * 2 + 3);
        mutate(i * 2 + 2);
        mutate(i * 2 + 3);
    }

    sortComplexities();
    updateComplexities();
}
